package com.vhall.component.service.exam.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Splitter;
import com.vhall.component.dao.exam.ExamAnswersMapper;
import com.vhall.component.dao.exam.ExamMapper;
import com.vhall.component.dao.exam.ExamRoomLkMapper;
import com.vhall.component.entity.exam.ExamAnswerEntity;
import com.vhall.component.entity.exam.dto.ExamListDTO;
import com.vhall.component.entity.exam.vo.ExamConsoleListReqVo;
import com.vhall.component.entity.exam.vo.ExamConsoleListRspVO;
import com.vhall.component.entity.exam.vo.ExamDeleteConsoleReqVo;
import com.vhall.component.entity.page.vo.PageVO;
import com.vhall.component.entity.room.RoomExamLkEntity;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.service.exam.ExamConsoleService;
import com.vhall.component.service.exam.ExamService;
import com.vhall.component.service.exam.constant.ExamConst;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author guoxin.wang
 * @since 2021/8/19 17:28
 */
@Service
public class ExamConsoleServiceImpl implements ExamConsoleService {

    @Autowired
    private ExamService examService;

    @Autowired
    private ExamRoomLkMapper examRoomLkMapper;

    @Autowired
    private ExamAnswersMapper examAnswersMapper;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final List<Integer> STATUS = Arrays.asList(ExamConst.LK_STATUS_NOT_PUBLISH, ExamConst.LK_STATUS_PUBLISH,
            ExamConst.LK_STATUS_FINISH, ExamConst.LK_STATUS_GRADED, ExamConst.LK_STATUS_PUSH);

    /**
     * 由于创建试卷不用关联房间所以，作为列表没有用户和房间权限，所以、
     * 用了left join 要了exam全量
     *
     * @param vo
     * @return
     */
    @Override
    public PageVO<ExamConsoleListRspVO> list(ExamConsoleListReqVo vo) {
        String endTime = vo.getEndTime();
        // 构建查询参数
        Page<ExamListDTO> pageParam = new Page<>();
        pageParam.setCurrent(vo.getPage());
        pageParam.setSize(vo.getPageSize());
        // 设置类型是考试相关
        Map<String, Object> param = new HashMap<>(16);
        Map<String, Object> exams = new HashMap<>(16);
        exams.put("type", ExamConst.TYPE_EXAM);
        Map<String, Object> roomExamLk = new HashMap<>(16);
        if (StringUtils.isNotBlank(vo.getRoomId())) {
            roomExamLk.put("roomId", vo.getRoomId());
        }
        if (vo.getAccountId() != null) {
            roomExamLk.put("accountId", vo.getAccountId());
        }
        if (vo.getPublish() != null) {
            roomExamLk.put("publish", vo.getPublish());
        }
        if (vo.getIsFinish() != null) {
            roomExamLk.put("isFinish", vo.getIsFinish());
        }
        if (vo.getIsGrade() != null) {
            roomExamLk.put("isGrade", vo.getIsGrade());
        }
        if (vo.getIsPushGrade() != null) {
            roomExamLk.put("isPushGrade", vo.getIsPushGrade());
        }
        if (StringUtils.isNotBlank(vo.getKeyword()) && StringUtils.isNotBlank(StringUtils.toStringTrim(vo.getKeyword()))) {
            param.put("keyword", vo.getKeyword());
        }
        if (StringUtils.isNotBlank(vo.getBeginTime())) {
            param.put("beginTime", vo.getBeginTime());
        }
        if (StringUtils.isNotBlank(vo.getEndTime())) {
            endTime = getEndtime(endTime);
            param.put("endTime", endTime);
        }
        if (vo.getAnswerAccountId() != null) {
            roomExamLk.put("answerAccountId", vo.getAnswerAccountId());
        }
        param.put("exams", exams);
        param.put("roomExamLk", roomExamLk);

        if (vo.getStatus() != null) {
            getConditionByStatus(param, vo.getStatus());
        }
        IPage<ExamConsoleListRspVO> list;
        // 开始查询
        if (StringUtils.isNotBlank(vo.getRoomId())) {
            if (vo.getStatus() != null && vo.getStatus() == 2) {
                list = examRoomLkMapper.joinExamsList2(pageParam, param);
            } else {
                list = examRoomLkMapper.joinExamsList(pageParam, param);
            }
        } else {
            list = examRoomLkMapper.joinExamsAndRoomsList(pageParam, param);
        }
        if (list == null || CollectionUtils.isEmpty(list.getRecords())) {
            return new PageVO<ExamConsoleListRspVO>(0, 0, 0, new ArrayList());
        }
        List<ExamConsoleListRspVO> records = list.getRecords();
        List<Integer> examIds = records.stream().map(ExamConsoleListRspVO::getExamId).collect(Collectors.toList());
        // 查询过是否回答过试卷
        Map<Integer, Integer> mapOfAnswer = new HashMap<>(16);
        if (vo.getAnswerAccountId() != null && StringUtils.isNotBlank(vo.getRoomId())) {
            LambdaQueryWrapper<ExamAnswerEntity> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(ExamAnswerEntity::getRoomId, vo.getRoomId());
            wrapper.eq(ExamAnswerEntity::getAccountId, vo.getAnswerAccountId());
            wrapper.in(ExamAnswerEntity::getExamId, examIds);
            List<ExamAnswerEntity> examAnswerEntities = examAnswersMapper.selectList(wrapper);
            if (CollectionUtils.isNotEmpty(examAnswerEntities)) {
                mapOfAnswer = examAnswerEntities.stream().collect(Collectors.toMap(ExamAnswerEntity::getExamId, ExamAnswerEntity::getAnswerId));
            }
        }
        // 最后组装数据
        String key = "";
        for (ExamConsoleListRspVO single : records) {
            ExamListDTO paramOfStatus = new ExamListDTO();
            paramOfStatus.setLkPublish(single.getPublish());
            paramOfStatus.setLkIsFinish(single.getIsFinish());
            paramOfStatus.setLkIsGrade(single.getIsGrade());
            paramOfStatus.setLkIsPushGrade(single.getIsPushGrade());
            single.setStatus(examService.examStatus(paramOfStatus));
            single.setAnswer(mapOfAnswer.containsKey(single.getExamId()) ? 1 : 0);
            single.setAnswerId(mapOfAnswer.getOrDefault(single.getExamId(), 0));
            //观众提交答案至队列处理有时间差 因此使用缓存判断是否提交
            if (vo.getAnswerAccountId() != null && ExamConst.LK_STATUS_PUBLISH.equals(single.getStatus())) {
                key = ExamConst.EXAM_ANSWER + "lock:" + single.getExamId() + "-" + single.getRoomId() + "-" + vo.getAnswerAccountId();
                String s = redisTemplate.opsForValue().get(key);
                if (StringUtils.isNotBlank(s)) {
                    single.setAnswer(Integer.valueOf(s));
                }
            }
            //观众获取考试中列表 如果已作答则不展示 如无需此逻辑可删除---在这里先注释掉。因为sql已经分页了，所以这里不能再删除数据。
            /*if ($item['answer'] && $params['answer_account_id'] && $params['status'] == ExamConstant::LK_STATUS_PUBLISH) {
                unset($list[$k]);
            }*/
        }
        return new PageVO<>(vo.getPage(), vo.getPageSize(), (int) list.getTotal(), records);
    }

    /**
     * 结束时间格式化
     *
     * @param endTime 结束时间
     * @return
     */
    private String getEndtime(String endTime) {
        //如果格式为yyyy-MM-dd，就直接加字符串;如果格式为yyyy-MM-dd hh:mm:ss,先截取字符串，再拼串
        if (endTime.length() > 10) {
            endTime = endTime.substring(0, 11);
        }
        return endTime + " 23:59:59";
    }


    /**
     * 根据status获取对应条件
     *
     * @param param
     * @param status
     * @return array
     */
    private void getConditionByStatus(Map<String, Object> param, Integer status) {
        if (!STATUS.contains(status)) {
            return;
        }
        Map roomExamLk = (Map) param.get("roomExamLk");
        switch (status) {
            case 1:
                roomExamLk.put("publish", 0);
                break;
            case 2:
                roomExamLk.put("publish", 1);
                roomExamLk.put("isFinish", 0);
                break;
            case 3:
                roomExamLk.put("isFinish", 1);
                roomExamLk.put("isGrade", 0);
                break;
            case 4:
                roomExamLk.put("isGrade", 1);
                roomExamLk.put("isPushGrade", 0);
                break;
            case 5:
                roomExamLk.put("isPushGrade", 1);
                break;
            default:
                break;
        }
    }


    @Override
    public Integer delete(Integer accountId, ExamDeleteConsoleReqVo vo) {
        String idss = vo.getExamIds();
        if (idss.indexOf(",") <= 0) {
            try {
                Integer.valueOf(idss);
            } catch (Exception e) {
                throw new BusinessException(BizErrorCode.BIZ_INVALID_PARAM);
            }
        }
        List<Integer> idList = Splitter.on(",").splitToList(vo.getExamIds()).stream().map(Integer::valueOf).collect(Collectors.toList());
        LambdaQueryWrapper<RoomExamLkEntity> query = Wrappers.lambdaQuery();
        query.eq(RoomExamLkEntity::getAccountId, accountId);
        query.in(RoomExamLkEntity::getExamId, idList);
        query.gt(RoomExamLkEntity::getPublish, 0);
        List<RoomExamLkEntity> ress = examRoomLkMapper.selectList(query);
        if (CollectionUtils.isNotEmpty(ress)) {
            throw new BusinessException(BizErrorCode.COMP_EXAM_NOT_EDITABLE);
        }
        query.clear();
        query.in(RoomExamLkEntity::getExamId, idList);
        examRoomLkMapper.delete(query);
        int i = examMapper.deleteBatchIds(idList);
        return i;
    }
}
